package com.hivesight.infra.log4j;

import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.helpers.CyclicBuffer;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.TriggeringEventEvaluator;

/**
 * This Log4J appender will send its messages to a AppEngineLogger application - 
 * 
 * @author Elad Kehat - HiveSight.com
 *
 */

public class AppEngineAppender extends AppenderSkeleton {

	private static int BUFFER_SIZE = 1000;
	
	private CyclicBuffer cb;
	private String urlTemplate;
	private String apiKey;
	private String secretKey;
	private String applicationName;
	private TriggeringEventEvaluator evaluator = new DefaultEvaluator();
	
	public void setUrlTemplate(String urlTemplate) {
		this.urlTemplate = urlTemplate;
	}
	
	public void setApiKey(String key) {
		this.apiKey = key;
	}
	
	public void setSecretKey(String key) {
		this.secretKey = key;
	}
	
	public void setApplicationName(String appName) {
		this.applicationName = appName;
	}

	@Override
	public void activateOptions() {
		try {
			cb = new CyclicBuffer(BUFFER_SIZE);						
		} catch (Exception e) {
			errorHandler.error("Error starting appender ["+ name + "]", e, ErrorCode.GENERIC_FAILURE);
		}
	}

	@Override
	protected void append(LoggingEvent event) {
		if (!checkEntryConditions()) {
			return;
		}

		cb.add(event);
		if (evaluator.isTriggeringEvent(event)) {
			sendBuffer();
		}
	}

	@Override
	public void close() {

	}

	@Override
	public boolean requiresLayout() {
		return false;
	}

	private boolean checkEntryConditions() {
		if (urlTemplate == null) {
			errorHandler.error("No 'urlTemplate' for [" + name + "]");
			return false;						
		}
		
		if (apiKey == null) {
			errorHandler.error("No 'apiKey' for appender named [" + name + "]");
			return false;
		}

		if (secretKey == null) {
			errorHandler.error("No 'secretKey' for appender named [" + name + "]");
			return false;
		}		
		return true;
	}
	
	private void sendBuffer() {
	    try {
	        int len = cb.length();
	        for (int i = 0; i < len; i++) {
	            LoggingEvent event = cb.get();
				sendEvent(event);
	        }

	    } catch(Exception e) {
	        errorHandler.error("Could not send message from AppEngineAppender [" + name + "]", e, ErrorCode.GENERIC_FAILURE);
	    }
	}
	
	private void sendEvent(LoggingEvent event) throws Exception {
		List<NameValuePair> params = new ArrayList<NameValuePair>();
 		if (applicationName != null) {
			params.add(new NameValuePair("app_name", applicationName));
 		}
		else if (event.getThreadName() != null) {
			params.add(new NameValuePair("app_name", event.getThreadName()));
		}
		params.add(new NameValuePair("logger_name", event.getLoggerName()));
		params.add(new NameValuePair("message", event.getRenderedMessage()));
		params.add(new NameValuePair("severity", event.getLevel().toString()));
		String[] stack = event.getThrowableStrRep();
		if ((stack != null) && (stack.length > 0)) {
			StringBuilder sb = new StringBuilder();
			for (String s : stack) {
				sb.append(s).append("\n");
			}
			params.add(new NameValuePair("stack_dump", sb.toString()));
		}

		AppEngineSender.getInstance(apiKey, secretKey, urlTemplate).send(params);
	}
	
}
